using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Threading;
using System.IO;
using System.Xml;
using System.Xml.XPath;
using System.IO.IsolatedStorage;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Permissions;
using System.Reflection;
using System.Runtime.Serialization.Formatters;

namespace SPTraceView.Model
{
    internal enum TraceViewFarmRole
    {
        StandAlone,
        Server,
        Client
    }

    public sealed class TraceSettings : SerializationBinder
    {

        public sealed class FarmRoleSettings
        {
            internal TraceViewFarmRole m_FarmRole;
            internal string m_BroadcastAddress;
            internal bool m_UseMulticastGroup;
            internal bool m_UseForeignFarm;
            internal Guid m_ForeignFarmID;
            internal bool m_ServerUseMulticast;

            internal FarmRoleSettings()
            {
                m_FarmRole = TraceViewFarmRole.StandAlone;
                m_BroadcastAddress = string.Empty;
                m_UseMulticastGroup = true;
                m_UseForeignFarm = false;
                m_ServerUseMulticast = true;
                m_ForeignFarmID = Guid.Empty;
            }

            internal void CopyFrom(FarmRoleSettings copyFrom)
            {
                if (copyFrom != null)
                {
                    m_FarmRole = copyFrom.m_FarmRole;
                    m_BroadcastAddress = copyFrom.m_BroadcastAddress;
                    m_UseMulticastGroup = copyFrom.m_UseMulticastGroup;
                    m_UseForeignFarm = copyFrom.m_UseForeignFarm;
                    m_ForeignFarmID = copyFrom.m_ForeignFarmID;
                    m_ServerUseMulticast = copyFrom.m_ServerUseMulticast;
                }
            }
        }

        [Serializable]
        public sealed class SerializableTraceSettings : ISerializable
        {
            internal List<ULS.TraceSeverity> m_MonitoredLevels = new List<ULS.TraceSeverity>();

            internal uint m_MonitoredLevelsBitmask = 0;

            internal FarmRoleSettings m_FarmRoleSettings = new FarmRoleSettings();

            internal string m_MessageIncludeFilter;
            internal string m_MessageExcludeFilter;
            internal Regex m_MessageIncludeRegex;
            internal Regex m_MessageExcludeRegex;
            internal bool m_IncludeAllMessages = true;

            internal string m_CategoryIncludeFilter;
            internal string m_CategoryExcludeFilter;
            internal Regex m_CategoryIncludeRegex;
            internal Regex m_CategoryExcludeRegex;
            internal bool m_IncludeAllCategories = true;

            internal string m_ProductIncludeFilter;
            internal string m_ProductExcludeFilter;
            internal Regex m_ProductIncludeRegex;
            internal Regex m_ProductExcludeRegex;
            internal bool m_IncludeAllProducts = true;

            internal string m_ProcessIncludeFilter;
            internal Regex m_ProcessIncludeRegex;
            internal bool m_IncludeAllProcesses = true;

            internal bool m_TraceToDebugView = true;
            internal bool m_TraceToMyEventsLog = true;
            internal bool m_TraceShowBalloone = true;

            [NonSerialized]
            internal HistoryList m_Last10IncludeProcesses = new HistoryList(10, "PrcsInc");

            [NonSerialized]
            internal HistoryList m_Last10IncludeMessages = new HistoryList(10, "MsgInc");

            [NonSerialized]
            internal HistoryList m_Last10ExcludeMessages = new HistoryList(10, "MsgExc");

            [NonSerialized]
            internal HistoryList m_Last10IncludeProducts = new HistoryList(10, "ProdInc");

            [NonSerialized]
            internal HistoryList m_Last10ExcludeProducts = new HistoryList(10, "ProdExc");

            [NonSerialized]
            internal HistoryList m_Last10IncludeCategories = new HistoryList(10, "CatInc");

            [NonSerialized]
            internal HistoryList m_Last10ExcludeCategories = new HistoryList(10, "CatEx");

            internal SerializableTraceSettings()
            { }

            internal void CopyHistoryFrom(SerializableTraceSettings copyFrom)
            {
                m_Last10IncludeProcesses.CopyFrom(copyFrom.m_Last10IncludeProcesses);
                m_Last10IncludeMessages.CopyFrom(copyFrom.m_Last10IncludeMessages);
                m_Last10ExcludeMessages.CopyFrom(copyFrom.m_Last10ExcludeMessages);
                m_Last10IncludeProducts.CopyFrom(copyFrom.m_Last10IncludeProducts);
                m_Last10ExcludeProducts.CopyFrom(copyFrom.m_Last10ExcludeProducts);
                m_Last10IncludeCategories.CopyFrom(copyFrom.m_Last10IncludeCategories);
                m_Last10ExcludeCategories.CopyFrom(copyFrom.m_Last10ExcludeCategories);
            }

            #region ISerializable Members

            public SerializableTraceSettings(SerializationInfo info, StreamingContext context)
            {
                Debug.WriteLine("SerializableTraceSettings(ISerializable)");

                int version = info.GetInt32("VERSION");

                int cnt = info.GetInt32("MonitoredLevelsCount");

                Debug.WriteLine("m_MonitoredLevels.Count: " + cnt.ToString());

                m_MonitoredLevels.Clear();
                for (int i = 0; i < cnt; i++)
                {
                    int severityAsInt = info.GetInt32(string.Format("MonitoredLevelsCount_{0}", i));
                    m_MonitoredLevels.Add((ULS.TraceSeverity)severityAsInt);
                }

                m_MonitoredLevelsBitmask = info.GetUInt32("MonitoredLevelsBitmask");

                m_MessageIncludeFilter = info.GetString("MessageIncludeFilter");
                m_MessageExcludeFilter = info.GetString("MessageExcludeFilter");
                m_IncludeAllMessages = info.GetBoolean("IncludeAllMessages");
                m_CategoryIncludeFilter = info.GetString("CategoryIncludeFilter");
                m_CategoryExcludeFilter = info.GetString("CategoryExcludeFilter");
                m_IncludeAllCategories = info.GetBoolean("IncludeAllCategories");
                m_ProductIncludeFilter = info.GetString("ProductIncludeFilter");
                m_ProductExcludeFilter = info.GetString("ProductExcludeFilter");
                m_IncludeAllProducts = info.GetBoolean("IncludeAllProducts");
                m_ProcessIncludeFilter = info.GetString("ProcessIncludeFilter");
                m_IncludeAllProcesses = info.GetBoolean("IncludeAllProcesses");

                m_TraceToDebugView = info.GetBoolean("TraceToDebugView");
                m_TraceToMyEventsLog = info.GetBoolean("TraceToMyEventsLog");
                m_TraceShowBalloone = info.GetBoolean("TraceShowBalloone");

                m_Last10IncludeProcesses.Deserialize(info, context);
                m_Last10IncludeMessages.Deserialize(info, context);
                m_Last10ExcludeMessages.Deserialize(info, context);
                m_Last10IncludeProducts.Deserialize(info, context);
                m_Last10ExcludeProducts.Deserialize(info, context);
                m_Last10IncludeCategories.Deserialize(info, context);
                m_Last10ExcludeCategories.Deserialize(info, context);
     
                if (version > 1)
                {
                    m_FarmRoleSettings.m_BroadcastAddress = info.GetString("Farm_BroadcastAddress");
                    m_FarmRoleSettings.m_FarmRole = (TraceViewFarmRole)info.GetInt32("Farm_FarmRole");
                    m_FarmRoleSettings.m_UseMulticastGroup = info.GetBoolean("Farm_UseMulticast");
                    m_FarmRoleSettings.m_UseForeignFarm = info.GetBoolean("Farm_ForeignFarm");
                    m_FarmRoleSettings.m_ForeignFarmID = new Guid(info.GetString("Farm_ForeignFarmID"));
                    m_FarmRoleSettings.m_ServerUseMulticast = info.GetBoolean("Farm_ServerUseMulticast");
                }
            }

            void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
            {
                Debug.WriteLine("SerializableTraceSettings.ISerializable.GetObjectData");

                info.AddValue("VERSION", 2);

                Debug.WriteLine("m_MonitoredLevels.Count: " + m_MonitoredLevels.Count.ToString());

                info.AddValue("MonitoredLevelsCount", m_MonitoredLevels.Count);
                for (int i = 0; i < m_MonitoredLevels.Count; i++)
                {
                    ULS.TraceSeverity severity = m_MonitoredLevels[i];
                    int intSeverity = (int)severity;
                    info.AddValue(string.Format("MonitoredLevelsCount_{0}", i), intSeverity);
                }

                info.AddValue("MonitoredLevelsBitmask", m_MonitoredLevelsBitmask);

                info.AddValue("MessageIncludeFilter", m_MessageIncludeFilter);
                info.AddValue("MessageExcludeFilter", m_MessageExcludeFilter);
                info.AddValue("IncludeAllMessages", m_IncludeAllMessages);
                info.AddValue("CategoryIncludeFilter", m_CategoryIncludeFilter);
                info.AddValue("CategoryExcludeFilter", m_CategoryExcludeFilter);
                info.AddValue("IncludeAllCategories", m_IncludeAllCategories);
                info.AddValue("ProductIncludeFilter", m_ProductIncludeFilter);
                info.AddValue("ProductExcludeFilter", m_ProductExcludeFilter);
                info.AddValue("IncludeAllProducts", m_IncludeAllProducts);
                info.AddValue("ProcessIncludeFilter", m_ProcessIncludeFilter);
                info.AddValue("IncludeAllProcesses", m_IncludeAllProcesses);

                info.AddValue("TraceToDebugView", m_TraceToDebugView);
                info.AddValue("TraceToMyEventsLog", m_TraceToMyEventsLog);
                info.AddValue("TraceShowBalloone", m_TraceShowBalloone);

                m_Last10IncludeProcesses.Serialize(info, context);
                m_Last10IncludeMessages.Serialize(info, context);
                m_Last10ExcludeMessages.Serialize(info, context);
                m_Last10IncludeProducts.Serialize(info, context);
                m_Last10ExcludeProducts.Serialize(info, context);
                m_Last10IncludeCategories.Serialize(info, context);
                m_Last10ExcludeCategories.Serialize(info, context);

                #region VERSION 2 SETTINGS
                info.AddValue("Farm_BroadcastAddress", m_FarmRoleSettings.m_BroadcastAddress);
                info.AddValue("Farm_FarmRole", (int)m_FarmRoleSettings.m_FarmRole);
                info.AddValue("Farm_UseMulticast", m_FarmRoleSettings.m_UseMulticastGroup);
                info.AddValue("Farm_ForeignFarm", m_FarmRoleSettings.m_UseForeignFarm);
                info.AddValue("Farm_ForeignFarmID", m_FarmRoleSettings.m_ForeignFarmID.ToString("B"));
                info.AddValue("Farm_ServerUseMulticast", m_FarmRoleSettings.m_ServerUseMulticast);
                #endregion
            }

            #endregion
        }

        private object m_SyncRoot = new object();

        private SerializableTraceSettings m_Settings = new SerializableTraceSettings();

        private List<string> m_AllKnownExes = new List<string>();
        private List<string> m_AllKnownProducts = new List<string>();
        private List<string> m_AllKnownCategories = new List<string>();
        

        private bool m_IsLive = false;
        private XmlDocument m_XmlCache = null;

#if DEBUG
        private bool m_AllKnownListsDirty = false;
        private string m_XmlCacheFileName = null;
#endif

        private const string BLANK_CACH_XML = "<SPTraceViewCache><KnownExeNames /> <KnownProductNames /> <KnownCategoryNames /></SPTraceViewCache>";

        public TraceSettings(bool isLive)
        {
            m_IsLive = isLive;

            if (isLive)
            {
                m_XmlCache = new XmlDocument();
#if DEBUG
                m_XmlCacheFileName = Path.GetFullPath(AppDomain.CurrentDomain.BaseDirectory + "\\SPTraceView.Cache.xml");
                if (File.Exists(m_XmlCacheFileName))
                {
                    try
                    {
                        m_XmlCache.Load(m_XmlCacheFileName);
                    }
                    catch (Exception ex)
                    {
                        Trace.WriteLine(ex.GetType().ToString() + " : " + ex.Message + "\r\n" + ex.StackTrace);

                        m_XmlCache.LoadXml(BLANK_CACH_XML);
                        try
                        {
                            m_XmlCache.Save(m_XmlCacheFileName);
                        }
                        catch (Exception secondEx)
                        {
                            Trace.WriteLine(secondEx.GetType().ToString() + " : " + secondEx.Message + "\r\n" + secondEx.StackTrace);
                            m_XmlCache = null;
                        }
                    }
                }
                else
                {
                    m_XmlCache.LoadXml(BLANK_CACH_XML);
                    try
                    {
                        m_XmlCache.Save(m_XmlCacheFileName);
                    }
                    catch (Exception ex)
                    {
                        Trace.WriteLine(ex.GetType().ToString() + " : " + ex.Message + "\r\n" + ex.StackTrace);
                        m_XmlCache = null;
                    }
                }

                // Live settings update the exename, product name and category name
                ThreadPool.QueueUserWorkItem(OnLiveSettingsTick);
#endif
            }
            else
            {
                // Non live settings don't update, but can be used to load and save the settings
                SetDefaults();
                Load();
            }
        }

        public void CopyFrom(TraceSettings copyFrom)
        {
            Debug.WriteLine("TraceSettngs.CopyFrom");

            Debug.Assert(!copyFrom.m_IsLive);

            m_Settings.m_MonitoredLevels.Clear();
            m_Settings.m_MonitoredLevels.AddRange(copyFrom.m_Settings.m_MonitoredLevels);
            m_Settings.m_MonitoredLevelsBitmask = copyFrom.m_Settings.m_MonitoredLevelsBitmask;

            MessageIncludeFilter = copyFrom.MessageIncludeFilter;
            MessageExcludeFilter = copyFrom.MessageExcludeFilter;

            CategoryIncludeFilter = copyFrom.CategoryIncludeFilter;
            CategoryExcludeFilter = copyFrom.CategoryExcludeFilter;

            ProductIncludeFilter = copyFrom.ProductIncludeFilter;
            ProductExcludeFilter = copyFrom.ProductExcludeFilter;

            ProcessIncludeFilter = copyFrom.ProcessIncludeFilter;
            IncludeAllProcesses = copyFrom.IncludeAllProcesses;

            m_Settings.CopyHistoryFrom(copyFrom.m_Settings);
            m_Settings.m_FarmRoleSettings.CopyFrom(copyFrom.m_Settings.m_FarmRoleSettings);
        }

        internal IEnumerable<ULS.TraceSeverity> MonitoredLevels
        {
            get { return m_Settings.m_MonitoredLevels; }
        }

        internal void AddMonitoredLevel(ULS.TraceSeverity level)
        {
            if (m_Settings.m_MonitoredLevels.IndexOf(level) == -1)
            {
                uint levelBitmask = TraceSeverityBitmask(level);
                m_Settings.m_MonitoredLevels.Add(level);
                m_Settings.m_MonitoredLevelsBitmask |= levelBitmask;
            }
        }

        internal void RemoveMonitoredLevel(ULS.TraceSeverity level)
        {
            if (m_Settings.m_MonitoredLevels.IndexOf(level) != -1)
            {
                uint levelBitmask = TraceSeverityBitmask(level);
                m_Settings.m_MonitoredLevels.Remove(level);
                m_Settings.m_MonitoredLevelsBitmask &= ~levelBitmask;
            }
        }

        public string MessageIncludeFilter
        {
            get { return m_Settings.m_MessageIncludeFilter; }
            set 
            {
                if (m_Settings.m_MessageIncludeFilter != value)
                {
                    m_Settings.m_MessageIncludeFilter = value;
                    m_Settings.m_MessageIncludeRegex = InitRegex(value, true);
                    m_Settings.m_IncludeAllMessages = value == "*";
                    m_Settings.m_Last10IncludeMessages.Add(value);
                }
            }
        }

        public string MessageExcludeFilter
        {
            get { return m_Settings.m_MessageExcludeFilter; }
            set
            {
                if (m_Settings.m_MessageExcludeFilter != value)
                {
                    m_Settings.m_MessageExcludeFilter = value;
                    m_Settings.m_MessageExcludeRegex = InitRegex(value, true);
                    m_Settings.m_Last10ExcludeMessages.Add(value);
                }
            }
        }

        public string ProductIncludeFilter
        {
            get { return m_Settings.m_ProductIncludeFilter; }
            set
            {
                if (m_Settings.m_ProductIncludeFilter != value)
                {
                    m_Settings.m_ProductIncludeFilter = value;
                    m_Settings.m_ProductIncludeRegex = InitRegex(value, true);
                    m_Settings.m_IncludeAllProducts = value == "*";
                    m_Settings.m_Last10IncludeProducts.Add(value);
                }
            }
        }

        public string ProductExcludeFilter
        {
            get { return m_Settings.m_ProductExcludeFilter; }
            set
            {
                if (m_Settings.m_ProductExcludeFilter != value)
                {
                    m_Settings.m_ProductExcludeFilter = value;
                    m_Settings.m_ProductExcludeRegex = InitRegex(value, true);
                    m_Settings.m_Last10ExcludeProducts.Add(value);
                }
            }
        }

        public string CategoryIncludeFilter
        {
            get { return m_Settings.m_CategoryIncludeFilter; }
            set
            {
                if (m_Settings.m_CategoryIncludeFilter != value)
                {
                    m_Settings.m_CategoryIncludeFilter = value;
                    m_Settings.m_CategoryIncludeRegex = InitRegex(value, true);
                    m_Settings.m_IncludeAllCategories = value == "*";
                    m_Settings.m_Last10IncludeCategories.Add(value);
                }
            }
        }

        public string CategoryExcludeFilter
        {
            get { return m_Settings.m_CategoryExcludeFilter; }
            set
            {
                if (m_Settings.m_CategoryExcludeFilter != value)
                {
                    m_Settings.m_CategoryExcludeFilter = value;
                    m_Settings.m_CategoryExcludeRegex = InitRegex(value, true);
                    m_Settings.m_Last10ExcludeCategories.Add(value);
                }
            }
        }

        public bool IncludeAllProcesses
        {
            get { return m_Settings.m_IncludeAllProcesses; }
            set 
            {
                m_Settings.m_IncludeAllProcesses = value;

                if (value)
                    m_Settings.m_ProcessIncludeRegex = null;
                else
                    m_Settings.m_ProcessIncludeRegex = InitRegex(m_Settings.m_ProcessIncludeFilter, false);
            }
        }

        public string ProcessIncludeFilter
        {
            get { return m_Settings.m_ProcessIncludeFilter; }
            set
            {
                if (m_Settings.m_ProcessIncludeFilter != value)
                {
                    m_Settings.m_ProcessIncludeFilter = value;
                    m_Settings.m_ProcessIncludeRegex = InitRegex(value, false);
                    m_Settings.m_Last10IncludeProcesses.Add(value);
                }
            }
        }

        public bool TraceToDebugView
        {
            get { return m_Settings.m_TraceToDebugView; }
            set { m_Settings.m_TraceToDebugView = value; }
        }

        public bool TraceToMyEventsLog
        {
            get { return m_Settings.m_TraceToMyEventsLog; }
            set { m_Settings.m_TraceToMyEventsLog = value; }
        }

        public bool TraceShowBalloone
        {
            get { return m_Settings.m_TraceShowBalloone; }
            set { m_Settings.m_TraceShowBalloone = value; }
        }

        public FarmRoleSettings FarmRole
        {
            get { return m_Settings.m_FarmRoleSettings; }
        }

        internal HistoryList Last10IncludeProcesses
        {
            get { return m_Settings.m_Last10IncludeProcesses; }
        }

        internal HistoryList Last10IncludeMessages
        {
            get { return m_Settings.m_Last10IncludeMessages; }
        }

        internal HistoryList Last10ExcludeMessages
        {
            get { return m_Settings.m_Last10ExcludeMessages; }
        }

        internal HistoryList Last10IncludeProducts
        {
            get { return m_Settings.m_Last10IncludeProducts; }
        }

        internal HistoryList Last10ExcludeProducts
        {
            get { return m_Settings.m_Last10ExcludeProducts; }
        }

        internal HistoryList Last10IncludeCategories
        {
            get { return m_Settings.m_Last10IncludeCategories; }
        }

        internal HistoryList Last10ExcludeCategories
        {
            get { return m_Settings.m_Last10ExcludeCategories; }
        }

        internal bool IsMatch(ULS.ULSTraceHeader data, ULS.TraceSeverity severity)
        {
#if DEBUG
            TrackCategories(
                data.ExeName != null ? data.ExeName.ToLower() : null, 
                data.Product != null ? data.Product.ToLower() : null, 
                data.Category != null ? data.Category.ToLower() : null);
#endif
            uint severityBitmask = TraceSeverityBitmask(severity);
            if ((severityBitmask & m_Settings.m_MonitoredLevelsBitmask) == 0)
            {
                Debug.WriteLine(string.Format("Excluding event because severity doesn't match. Filter severity: {0}, event severity: {1}", Convert.ToString(m_Settings.m_MonitoredLevelsBitmask, 2), Convert.ToString(severityBitmask, 2), "SPTraceView"));
                return false;
            }

            if (!m_Settings.m_IncludeAllProcesses &&
                m_Settings.m_ProcessIncludeRegex != null &&
                !m_Settings.m_ProcessIncludeRegex.IsMatch(data.ExeName))
            {
                Debug.WriteLine("Excluding event because exe name include filter doesn't match", "SPTraceView");
                return false;
            }

            if (!m_Settings.m_IncludeAllProducts)
            {
                if (m_Settings.m_ProductIncludeRegex == null ||
                    !m_Settings.m_ProductIncludeRegex.IsMatch(data.Product))
                {
                    Debug.WriteLine("Excluding event because products include filter doesn't match", "SPTraceView");
                    return false;
                }
            }

            if (m_Settings.m_ProductExcludeRegex != null &&
                m_Settings.m_ProductExcludeRegex.IsMatch(data.Product))
            {
                Debug.WriteLine("Excluding event because products exclude filter matches", "SPTraceView");
                return false;
            }

            if (!m_Settings.m_IncludeAllCategories)
            {
                if (m_Settings.m_CategoryIncludeRegex == null ||
                    !m_Settings.m_CategoryIncludeRegex.IsMatch(data.Category))
                {
                    Debug.WriteLine("Excluding event because categories include filter doesn't match", "SPTraceView");
                    return false;
                }
            }

            if (m_Settings.m_CategoryExcludeRegex != null &&
                m_Settings.m_CategoryExcludeRegex.IsMatch(data.Category))
            {
                Debug.WriteLine("Excluding event because categories exclude filter matches", "SPTraceView");
                return false;
            }

            if (!m_Settings.m_IncludeAllMessages)
            {
                if (m_Settings.m_MessageIncludeRegex == null ||
                    !m_Settings.m_MessageIncludeRegex.IsMatch(data.Message))
                {
                    Debug.WriteLine("Excluding event because messages include filter doesn't match", "SPTraceView");
                    return false;
                }
            }

            if (m_Settings.m_MessageExcludeRegex != null &&
                m_Settings.m_MessageExcludeRegex.IsMatch(data.Message))
            {
                Debug.WriteLine("Excluding event because messages exclude filter matches", "SPTraceView");
                return false;
            }

            return true;
        }

        private uint TraceSeverityBitmask(ULS.TraceSeverity severity)
        {
            switch (severity)
            {
                case ULS.TraceSeverity.Assert:
                    return 0x0001;

                case ULS.TraceSeverity.CriticalEvent:
                    return 0x0002;

                case ULS.TraceSeverity.Exception:
                    return 0x0004;

                case ULS.TraceSeverity.High:
                    return 0x0008;

                case ULS.TraceSeverity.InformationEvent:
                    return 0x0010;

                case ULS.TraceSeverity.Medium:
                    return 0x0020;

                case ULS.TraceSeverity.Monitorable:
                    return 0x0040;

                case ULS.TraceSeverity.Unassigned:
                    return 0x0080;

                case ULS.TraceSeverity.Unexpected:
                    return 0x0100;

                case ULS.TraceSeverity.Verbose:
                    return 0x0200;

                case ULS.TraceSeverity.WarningEvent:
                    return 0x0400;
            }

            return 0;
        }

        public void Save()
        {
            Debug.WriteLine("TraceSettngs.Save");

            using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForAssembly())
            using (IsolatedStorageFileStream isoStream = new IsolatedStorageFileStream("SPTraceView.Settings", FileMode.Create, FileAccess.ReadWrite, file))
            {
                lock (m_SyncRoot)
                {
                    Debug.WriteLine("TraceSettngs.Save: " + isoStream.Length.ToString() + " bytes initial");

                    BinaryFormatter fmt = new BinaryFormatter();
                    fmt.Binder = this;
                    fmt.Serialize(isoStream, m_Settings);

                    Debug.WriteLine("TraceSettngs.Save: " + isoStream.Length.ToString() + " bytes saved");
                }
            }
        }

        private void Load()
        {
            Debug.WriteLine("TraceSettngs.Load");
            bool saveAfterLoad = false;

            using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForAssembly())
            using (IsolatedStorageFileStream isoStream = new IsolatedStorageFileStream("SPTraceView.Settings", FileMode.OpenOrCreate, FileAccess.ReadWrite, file))
            {
                Debug.WriteLine("TraceSettngs.Load: " + isoStream.Length.ToString() + " bytes");

                lock (m_SyncRoot)
                {
                    BinaryFormatter fmt = new BinaryFormatter();
                    try
                    {
                        fmt.Binder = this;
                        m_Settings = (SerializableTraceSettings)fmt.Deserialize(isoStream);
                    }
                    catch // (Exception ex)
                    {
                        if (m_Settings == null)
                            m_Settings = new SerializableTraceSettings();

                        saveAfterLoad = true;
                    }
                }

                
            }

            if (saveAfterLoad)
                Save();
        }

        public void SetDefaults()
        {
            Debug.WriteLine("TraceSettngs.SetDefaults");

            m_Settings.m_MonitoredLevels.Clear();
            AddMonitoredLevel(ULS.TraceSeverity.Assert);
            AddMonitoredLevel(ULS.TraceSeverity.CriticalEvent);
            AddMonitoredLevel(ULS.TraceSeverity.Exception);
            AddMonitoredLevel(ULS.TraceSeverity.Unexpected);
            AddMonitoredLevel(ULS.TraceSeverity.WarningEvent);
            AddMonitoredLevel(ULS.TraceSeverity.Unassigned);

            MessageIncludeFilter = "*";
            MessageExcludeFilter = string.Empty;

            CategoryIncludeFilter = "*";
            CategoryExcludeFilter = string.Empty;

            ProductIncludeFilter = "*";
            ProductExcludeFilter = string.Empty;

            m_Settings.m_IncludeAllProcesses = true;
            ProcessIncludeFilter = "owstimer.exe;w3wp.exe";

            m_Settings.m_TraceShowBalloone = true;
            m_Settings.m_TraceToDebugView = true;
            m_Settings.m_TraceToMyEventsLog = true;
        }

        private Regex InitRegex(string value, bool allowWildcards)
        {
            if (string.IsNullOrEmpty(value))
                return null;
            else
                return new Regex(WildcardFilterToRegexString(value, allowWildcards), RegexOptions.IgnoreCase);
        }

        private static string s_RegexSpecialChars = "[\\^$.|?*+(){}";

        private Regex m_CleanUpSpaces1Regex = new Regex("\\s+;|;\\s+", RegexOptions.IgnoreCase);
        private Regex m_CleanUpSpaces2Regex = new Regex("^\\s+|\\s+$", RegexOptions.IgnoreCase);
        
        private string WildcardFilterToRegexString(string filter, bool allowWildcards)
        {
            // Remove all spaces before and after ";", at the beginning or at the end
            filter = m_CleanUpSpaces1Regex.Replace(filter, ";"); /* once for '  ;' */
            filter = m_CleanUpSpaces1Regex.Replace(filter, ";"); /* and once for ';  ' */
            filter = m_CleanUpSpaces2Regex.Replace(filter, string.Empty);

            StringBuilder regexStr = new StringBuilder();

            bool prevCharIsAsterisk = false;

            regexStr.Append("^");

            char[] filterChars = filter.ToCharArray();

            for (int i = 0; i < filterChars.Length; i++)
            {
                char ch = filterChars[i];

                if (ch == '*')
                {
                    if (!prevCharIsAsterisk &&
                        allowWildcards)
                    {
                        regexStr.Append(".*");
                    }
                    prevCharIsAsterisk = true;
                }
                else
                {
                    prevCharIsAsterisk = false; 

                    if (ch == ';')
                    {
                        regexStr.Append("|");
                    }
                    else if (s_RegexSpecialChars.IndexOf(ch) != -1)
                    {
                        regexStr.Append("\\");
                        regexStr.Append(ch);
                    }
                    else
                    {
                        regexStr.Append(ch);
                    }
                }
            }

            regexStr.Append("$");

            return regexStr.ToString();
        }

#if DEBUG
        private void TrackCategories(string exeName, string productName, string categoryName)
        {
            lock (m_SyncRoot)
            {
                if (!string.IsNullOrEmpty(exeName) &&
                    m_AllKnownExes.IndexOf(exeName) == -1)
                {
                    m_AllKnownExes.Add(exeName);
                    m_AllKnownListsDirty = true;
                }

                if (!string.IsNullOrEmpty(productName) && 
                    m_AllKnownProducts.IndexOf(productName) == -1)
                {
                    m_AllKnownProducts.Add(productName);
                    m_AllKnownListsDirty = true;
                }

                if (!string.IsNullOrEmpty(categoryName) && 
                    m_AllKnownCategories.IndexOf(categoryName) == -1)
                {
                    m_AllKnownCategories.Add(categoryName);
                    m_AllKnownListsDirty = true;
                }
            }
        }
#endif

        internal void Stop()
        {
            // No need to syncrhonized access on "bool" members
            m_IsLive = false;

#if DEBUG
            SaveKnownListSynchronized();
#endif
        }

#if DEBUG
        private void OnLiveSettingsTick(object state)
        {
            while (m_IsLive)
            {
                // Sleep for 1 minute
                Thread.Sleep(60000);

                SaveKnownListSynchronized();
            }
        }

        private void SaveKnownListSynchronized()
        {
            if (m_AllKnownListsDirty)
            {
                lock (m_SyncRoot)
                {
                    if (m_AllKnownListsDirty)
                    {
                        try
                        {
                            SaveKnownLists();
                        }
                        catch (Exception ex)
                        {
                            Trace.WriteLine(ex.GetType().ToString() + " : " + ex.Message + "\r\n" + ex.StackTrace);
                        }

                        m_AllKnownListsDirty = false;
                    }
                }
            }
        }

        private void SaveKnownLists()
        {

            if (m_XmlCache != null &&
                m_XmlCacheFileName != null)
            {
                foreach (string exeName in m_AllKnownExes)
                {
                    if (m_XmlCache.SelectSingleNode(string.Format("//SPTraceViewCache/KnownExeNames/Exe[@name = '{0}']", exeName)) == null)
                    {
                        XmlNode knownExesNode = m_XmlCache.SelectSingleNode("//SPTraceViewCache/KnownExeNames");
                        XmlElement newExeNode = m_XmlCache.CreateElement("Exe");
                        XmlAttribute newExeNameAtt = m_XmlCache.CreateAttribute("name");
                        newExeNameAtt.Value = exeName;
                        newExeNode.Attributes.Append(newExeNameAtt);
                        knownExesNode.AppendChild(newExeNode);
                    }
                }

                foreach (string productName in m_AllKnownProducts)
                {
                    if (m_XmlCache.SelectSingleNode(string.Format("//SPTraceViewCache/KnownProductNames/Product[@name = '{0}']", productName)) == null)
                    {
                        XmlNode knownProductsNode = m_XmlCache.SelectSingleNode("//SPTraceViewCache/KnownProductNames");
                        XmlElement newProductNode = m_XmlCache.CreateElement("Product");
                        XmlAttribute newProductNameAtt = m_XmlCache.CreateAttribute("name");
                        newProductNameAtt.Value = productName;
                        newProductNode.Attributes.Append(newProductNameAtt);
                        knownProductsNode.AppendChild(newProductNode);
                    }
                }

                foreach (string categoryName in m_AllKnownCategories)
                {
                    if (m_XmlCache.SelectSingleNode(string.Format("//SPTraceViewCache/KnownCategoryNames/Category[@name = '{0}']", categoryName)) == null)
                    {
                        XmlNode knownCategoriesNode = m_XmlCache.SelectSingleNode("//SPTraceViewCache/KnownCategoryNames");
                        XmlElement newCategoryNode = m_XmlCache.CreateElement("Category");
                        XmlAttribute newCategoryNameAtt = m_XmlCache.CreateAttribute("name");
                        newCategoryNameAtt.Value = categoryName;
                        newCategoryNode.Attributes.Append(newCategoryNameAtt);
                        knownCategoriesNode.AppendChild(newCategoryNode);
                    }
                }

                m_XmlCache.Save(m_XmlCacheFileName);
            }            
        }
#endif

        public override Type BindToType(string assemblyName, string typeName)
        {
            // We need this when we run as loaded assembly
            return typeof(SerializableTraceSettings);
        }
    }

    internal sealed class TraceSeverityEntry
    {
        private ULS.TraceSeverity m_TraceSeverity;
        private string m_DisplayValue;

        public ULS.TraceSeverity TraceSeverity
        {
            get { return m_TraceSeverity; }
        }

        public string DisplayValue
        {
            get { return m_DisplayValue; }
        }

        private static List<TraceSeverityEntry> m_AllEntries = new List<TraceSeverityEntry>();

        private TraceSeverityEntry(ULS.TraceSeverity value, string displayValue)
        {
            m_TraceSeverity = value;
            m_DisplayValue = displayValue;
        }

        public override string ToString()
        {
            return m_DisplayValue;
        }

        static TraceSeverityEntry()
        {
            lock (m_AllEntries)
            {
                m_AllEntries.Add(new TraceSeverityEntry(ULS.TraceSeverity.CriticalEvent, "Critical Event"));
                m_AllEntries.Add(new TraceSeverityEntry(ULS.TraceSeverity.Unexpected, "Unexpected"));
                m_AllEntries.Add(new TraceSeverityEntry(ULS.TraceSeverity.Exception, "Exception"));
                m_AllEntries.Add(new TraceSeverityEntry(ULS.TraceSeverity.WarningEvent, "Warning Event"));
                m_AllEntries.Add(new TraceSeverityEntry(ULS.TraceSeverity.Assert, "Assert"));
                m_AllEntries.Add(new TraceSeverityEntry(ULS.TraceSeverity.Unassigned, "Unassigned"));
                m_AllEntries.Add(new TraceSeverityEntry(ULS.TraceSeverity.High, "High"));
                m_AllEntries.Add(new TraceSeverityEntry(ULS.TraceSeverity.Medium, "Medium"));
                m_AllEntries.Add(new TraceSeverityEntry(ULS.TraceSeverity.InformationEvent, "Information Event"));
                m_AllEntries.Add(new TraceSeverityEntry(ULS.TraceSeverity.Monitorable, "Monitorable"));
                m_AllEntries.Add(new TraceSeverityEntry(ULS.TraceSeverity.Verbose, "Verbose"));
            }
        }

        public static TraceSeverityEntry[] AllTraceSeverityEntries
        {
            get { return m_AllEntries.ToArray(); }
        }
    }
}
